/*
 * LevelMapImpl.java
 *
 * Created on 16. Dezember 2006, 17:25
 *
 * Copyright (c) 2007 Daniel Gronau
 *
 * This file is part of KanjiTori.
 *
 * KanjiTori is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * KanjiTori is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

package kanjitori.map;

import com.jme.bounding.BoundingBox;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.batch.TriangleBatch;
import com.jme.scene.geometryinstancing.GeometryBatchInstance;
import com.jme.scene.geometryinstancing.GeometryBatchInstanceAttributes;
import com.jme.scene.geometryinstancing.instance.GeometryBatchCreator;
import com.jme.scene.geometryinstancing.instance.GeometryBatchCreator;
import com.jme.scene.state.RenderState;
import com.jme.util.geom.BufferUtils;
import java.util.HashMap;
import kanjitori.map.RenderStateHolder;
import kanjitori.map.Map;
import kanjitori.map.AbstractMap;
import kanjitori.map.tile.Tile;

/**
 *
 * @author Pirx
 */
class DefaultMap extends AbstractMap implements Map {
    
    private Layer[] layers;
    private java.util.Map<String, RenderStateHolder> rsMap = new HashMap<String, RenderStateHolder>();
    private java.util.Map<Character, Tile> tileMap = new HashMap<Character, Tile>();
    
    /** Creates a new instance of LevelMapImpl
     * @param name
     * @param size
     * @param skyboxTextures
     * @param botCount
     */
    DefaultMap(String name, Position size, String[] skyboxTextures, int botCount) {
        super(name, size, skyboxTextures, botCount);
    }

    DefaultMap() {
    }
    
    void addRenderStateHolder(RenderStateHolder renderStateHolder) {
        rsMap.put(renderStateHolder.getName(), renderStateHolder);
    }
    
    void addTile(Tile tile) {
        tileMap.put(tile.getChar(), tile);
    }
    
    Tile getTile(char ref) {
        return tileMap.get(ref);
    }
    
    int getLayerCount() {
        return layers.length;
    }
    
    Layer getLayer(int index) {
        return layers[index];
    }
    
    void setLayers(Layer... layers) {
        for (Layer layer : layers) {
            if (! getSize().equals(layer.getSize())) {
                throw new IllegalArgumentException("Size of Map and Layer not equal");
            }
        }
        this.layers = layers;
    }
    
    public void init() {
        java.util.Map<RenderStateHolder, GeometryBatchCreator> gbMap = new HashMap<RenderStateHolder, GeometryBatchCreator>();
        for(RenderStateHolder rsHolder : rsMap.values()) {
            gbMap.put(rsHolder, new GeometryBatchCreator());
        }
        
        float[] angles = new float[3];
        
        for (Layer layer : layers) {
            for (int x = 0; x < getSize().x(); x++) {
                for (int y = 0; y < getSize().y(); y++) {
                    Tile tile = layer.getTile(Position.get(x, y));
                    Vector3f translation = new Vector3f(2 * x, layer.getHeight(), 2 * y);
                    if (tile != null) {
                        for (int i = 0; i < tile.getSize(); i++) {
                            RenderStateHolder rsHolder = rsMap.get(tile.getRenderStateName(i));
                            TriMesh mesh = tile.getMesh(i);
                            mesh.getLocalRotation().toAngles(angles);
                            GeometryBatchInstanceAttributes gbia = new GeometryBatchInstanceAttributes(
                                    translation.add(mesh.getLocalRotation().mult(mesh.getLocalTranslation())),
                                    Vector3f.UNIT_XYZ,
                                    new Vector3f(angles[0], angles[1], angles[2]),
                                    ColorRGBA.white);
                            for (int b = 0; b < mesh.getBatchCount(); b++) {
                                GeometryBatchInstance gi = new GeometryBatchInstance(mesh.getBatch(b), gbia);
                                GeometryBatchCreator gbCreator = gbMap.get(rsHolder);
                                gbCreator.addInstance(gi);
                            }
                        }
                    }
                }
            }
        }
        
        node = new Node();
        
        for (java.util.Map.Entry<RenderStateHolder, GeometryBatchCreator> entry : gbMap.entrySet()) {
            GeometryBatchCreator gbCreator = entry.getValue();
            if (gbCreator.getNumVertices() == 0) {
                continue;
            }
            TriMesh mesh = new TriMesh();
            TriangleBatch batch = mesh.getBatch(0);
            
            batch.setIndexBuffer(BufferUtils.createIntBuffer(gbCreator.getNumIndices()));
            batch.setVertexBuffer(BufferUtils.createVector3Buffer(gbCreator.getNumVertices()));
            batch.setNormalBuffer(BufferUtils.createVector3Buffer(gbCreator.getNumVertices()));
            batch.setTextureBuffer(BufferUtils.createVector2Buffer(gbCreator.getNumVertices()), 0);
            batch.setColorBuffer(BufferUtils.createFloatBuffer(gbCreator.getNumVertices() * 4));
            
            // Commit the instances to the mesh batch
            gbCreator.commit(batch);
            
            for (RenderState rs : entry.getKey().getRenderStates()) {
                mesh.setRenderState(rs);
            }
            
            mesh.updateRenderState();
            batch.setModelBound(new BoundingBox());
            mesh.updateModelBound();
            
            node.attachChild(mesh);
        }
        
        rsMap = null;
        tileMap = null;
        layers = null;
    }
  
    public String toString() {
        if (layers == null) {
            return getName();
        }
        StringBuilder sb = new StringBuilder();
        sb.append(this.getName() + "\n");
        for (Layer layer : layers) {
            sb.append(layer.getName()+ " h:" + layer.getHeight() + "\n");
            for (int y = getSize().y() - 1; y >= 0; y--) {
                for (int x = 0; x < getSize().x(); x++) {
                    Tile tile = layer.getTile(Position.get(x,y));
                    sb.append(tile == null ? ' ' : tile.getChar());
                }
                sb.append("|\n");
            }
        }
        sb.append("content\n");
        for (int y = getSize().y() - 1; y >= 0; y--) {
            for (int x = 0; x < getSize().x(); x++) {
                sb.append(getContentMatrix().getContent(Position.get(x,y)) == Content.EMPTY ? " " : "#");
            }
            sb.append("|\n");
        }
        return sb.toString();
    }

}
